package com.tianji.learning.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.query.PointsBoardQuery;
import com.tianji.learning.domain.vo.PointsBoardItemVO;
import com.tianji.learning.domain.vo.PointsBoardVO;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.learning.constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author friday
 * @since 2024-01-11
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;

    private final UserClient userClient;

    /**
     * 查询学霸积分排行榜
     * @param query
     * @return
     */
    @Override
    public PointsBoardVO queryPointsBoardList(PointsBoardQuery query) {
        //2.判断是当前赛季还是历史赛季 query.season  赛季id，为null或者为0则代表查询当前赛季
        Long season = query.getSeason(); //历史赛季id
        boolean isCurrent = season == null || season == 0; //为true 查看当前赛季 查redis
        LocalDate now = LocalDate.now();
        String format = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.POINTS_BOARD_KET_PREFIX + format;

        //3.查询我的排名  根据query.season 判断是查redis还是db
        PointsBoard myBoard = isCurrent ?
                queryMyCurrentBoard(key) :
                queryMyHistoryBoard(season);

        //4.分页查询赛季列表  根据query,season  判断查redis还是db
        List<PointsBoard> list = isCurrent ?
                queryCurrentBoard(key, query.getPageNo(), query.getPageSize()) :
                queryHistoryBoard(query);
        //5.封装vo
        PointsBoardVO vo = new PointsBoardVO();
        //5.1处理我的信息
        if (myBoard != null) {
            vo.setRank(myBoard.getRank()); //我的排名
            vo.setPoints(myBoard.getPoints()); //我的积分值
        }
        if (CollUtils.isEmpty(list)) {
            return vo;
        }
        //5.封装用户id集合 ，远程调用用户服务  获取用户信息 转map
        Set<Long> uIds = list.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userDTOS = userClient.queryUserByIds(uIds);
        if (CollUtils.isEmpty(userDTOS)){
            throw new BizIllegalException("用户不存在");
        }
        //转map  key为用户id  value为用户名称
        Map<Long, String> userDTOMap = userDTOS.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));

//        //6.封装vo返回
//        PointsBoardVO vo = new PointsBoardVO();
//        vo.setRank(myBoard.getRank()); //我的排名
//        vo.setPoints(myBoard.getPoints()); //我的积分值

        List<PointsBoardItemVO> voList = new ArrayList<>();
        for (PointsBoard p : list) {
            PointsBoardItemVO itemVO = new PointsBoardItemVO();
            itemVO.setPoints(p.getPoints());
            itemVO.setRank(p.getRank());
            itemVO.setName(userDTOMap.get(p.getUserId()));
            voList.add(itemVO);
        }
        vo.setBoardList(voList);
        return vo;
    }

    /**
     * 查询历史赛季 列表 查询db
     * @param query
     * @return
     */
    private List<PointsBoard> queryHistoryBoard(PointsBoardQuery query) {
        // 1.计算表名
        TableInfoContext.setInfo(POINTS_BOARD_TABLE_PREFIX + query.getSeason());
        // 2.查询数据
        Page<PointsBoard> page = page(query.toMpPage());
        // 3.数据处理
        List<PointsBoard> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return CollUtils.emptyList();
        }
        records.forEach(b -> b.setRank(b.getId().intValue()));
        return records;
    }

    /**
     * 查询当前赛季排行榜列表  查询redis（zset）
     * @param key
     * @param pageNo  页码
     * @param pageSize 条数
     * @return
     */
    public List<PointsBoard> queryCurrentBoard(String key, Integer pageNo, Integer pageSize) {
        //1.计算start 和 end 分页值
        int start = (pageNo-1)*pageSize;
        int end = start + pageSize-1;
        //2.利用zreverange排名  会按分数倒序  分页查询
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        if (CollUtils.isEmpty(typedTuples)){
            return CollUtils.emptyList();
        }
        //3.封装结果返回
        int rank = start + 1; //防止下一页的第一个排名接上上一页的排名
        List<PointsBoard> list = new ArrayList<>();
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            String value = typedTuple.getValue(); //用户id
            Double score = typedTuple.getScore(); //总积分值
            if (StringUtils.isBlank(value) || score == null){
                continue;
            }
            PointsBoard board = new PointsBoard();
            board.setUserId(Long.valueOf(value));
            board.setPoints((int)score.intValue());
            board.setRank(rank++);
            list.add(board);
        }
        return list;
    }

    /**
     * 查询历史赛季，我的积分和排名 查询db
     * @param season
     * @return
     */
    private PointsBoard queryMyHistoryBoard(Long season) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.计算表名
        TableInfoContext.setInfo(POINTS_BOARD_TABLE_PREFIX + season);
        // 3.查询数据
        Optional<PointsBoard> opt = lambdaQuery().eq(PointsBoard::getUserId, userId).oneOpt();
        if (opt.isEmpty()) {
            return null;
        }
        // 4.转换数据
        PointsBoard pointsBoard = opt.get();
        pointsBoard.setRank(pointsBoard.getId().intValue());
        return pointsBoard;
    }

    /**
     * 查询我的当前赛季的积分和排名 查询redis
     * @param key
     * @return
     */
    private PointsBoard queryMyCurrentBoard(String key) {
        Long userId = UserContext.getUser();
        //获取分值
        Double score = redisTemplate.opsForZSet().score(key, userId.toString());
        //获取排名 从0开始 需要 +1
        Long rank = redisTemplate.opsForZSet().reverseRank(key, userId.toString());
        PointsBoard board = new PointsBoard();
        board.setRank(rank==null ? 0 : rank.intValue()+1);
        board.setPoints(score==null ? 0 : score.intValue());
        return board;
    }
}
